function mxDefaultKeyHandler(editor)
{
	if (editor != null)
	{
		this.editor = editor;
		this.handler = new mxKeyHandler(editor.graph);

		var old = this.handler.escape;
		this.handler.escape = function(evt)
		{
			old.apply(this, arguments);
			editor.hideProperties();
			editor.fireEvent(mxEvent.ESCAPE, evt);
		};
	}
};
mxDefaultKeyHandler.prototype.editor = null;
mxDefaultKeyHandler.prototype.handler = null;
mxDefaultKeyHandler.prototype.bindAction = function(code, action, control)
{
	var self = this;
	var keyHandler = function()
	{
		self.editor.execute(action);
	};

	if (control)
	{
		this.handler.bindControlKey(code, keyHandler);
	}
	else
	{
		this.handler.bindKey(code, keyHandler);
	}
};
mxDefaultKeyHandler.prototype.destroy = function()
{
	this.handler.destroy();
	this.handler = null;
};

function mxDefaultPopupMenu(config)
{
	this.config = config;
};
mxDefaultPopupMenu.prototype.imageBasePath = null;
mxDefaultPopupMenu.prototype.config = null;
mxDefaultPopupMenu.prototype.createMenu = function(editor, menu, cell, evt)
{
	if (this.config != null)
	{
		var conditions = this.createConditions(editor, cell, evt);
		var item = this.config.firstChild;
		this.addItems(editor, menu, cell, evt, conditions, item, null);
	}
};
mxDefaultPopupMenu.prototype.addItems = function(editor, menu, cell, evt, conditions, item, parent)
{
	var addSeparator = false;

	while (item != null)
	{
		if (item.nodeName == 'add')
		{
			var condition = item.getAttribute('if');

			if (condition == null || conditions[condition])
			{
				var as = item.getAttribute('as');
				as = mxResources.get(as) || as;
				var funct = mxUtils.eval(mxUtils.getTextContent(item));
				var action = item.getAttribute('action');
				var icon = item.getAttribute('icon');

				if (addSeparator)
				{
					menu.addSeparator(parent);
					addSeparator = false;
				}

				if (icon != null && this.imageBasePath)
				{
					icon = this.imageBasePath + icon;
				}
				var row = this.addAction(menu, editor, as, icon, funct, action, cell, parent);
				this.addItems(editor, menu, cell, evt, conditions, item.firstChild, row);
			}
		}
		else if (item.nodeName == 'separator')
		{
			addSeparator = true;
		}
		item = item.nextSibling;
	}
};
mxDefaultPopupMenu.prototype.addAction = function(menu, editor, lab, icon, funct, action, cell, parent)
{
	var clickHandler = function()
	{
		if (typeof (funct) == 'function')
		{
			funct.call(editor, editor, cell);
		}

		if (action != null)
		{
			editor.execute(action, cell);
		}
	};
	return menu.addItem(lab, icon, clickHandler, parent);
};
mxDefaultPopupMenu.prototype.createConditions = function(editor, cell, evt)
{
	var model = editor.graph.getModel();
	var childCount = model.getChildCount(cell);
	var conditions = {};
	conditions['nocell'] = cell == null;
	conditions['ncells'] = editor.graph.getSelectionCount() > 1;
	conditions['notRoot'] = model.getRoot() != model.getParent(editor.graph.getDefaultParent());
	conditions['cell'] = cell != null;
	var isCell = cell != null && editor.graph.getSelectionCount() == 1;
	conditions['nonEmpty'] = isCell && childCount > 0;
	conditions['expandable'] = isCell && editor.graph.isCellFoldable(cell, false);
	conditions['collapsable'] = isCell && editor.graph.isCellFoldable(cell, true);
	conditions['validRoot'] = isCell && editor.graph.isValidRoot(cell);
	conditions['emptyValidRoot'] = conditions['validRoot'] && childCount == 0;
	conditions['swimlane'] = isCell && editor.graph.isSwimlane(cell);
	var condNodes = this.config.getElementsByTagName('condition');

	for (var i = 0; i < condNodes.length; i++)
	{
		var funct = mxUtils.eval(mxUtils.getTextContent(condNodes[i]));
		var name = condNodes[i].getAttribute('name');

		if (name != null && typeof (funct) == 'function')
		{
			conditions[name] = funct(editor, cell, evt);
		}
	}
	return conditions;
};

function mxDefaultToolbar(container, editor)
{
	this.editor = editor;

	if (container != null && editor != null)
	{
		this.init(container);
	}
};
mxDefaultToolbar.prototype.editor = null;
mxDefaultToolbar.prototype.toolbar = null;
mxDefaultToolbar.prototype.resetHandler = null;
mxDefaultToolbar.prototype.spacing = 4;
mxDefaultToolbar.prototype.connectOnDrop = false;
mxDefaultToolbar.prototype.init = function(container)
{
	if (container != null)
	{
		this.toolbar = new mxToolbar(container);

		var self = this;
		this.toolbar.addListener(mxEvent.SELECT, function(sender, evt)
		{
			var funct = evt.getArgAt(0);

			if (funct != null)
			{
				self.editor.insertFunction = function()
				{
					funct.apply(self, arguments);
					self.toolbar.resetMode();
				};
			}
			else
			{
				self.editor.insertFunction = null;
			}
		});
		this.resetHandler = function()
		{
			if (self.toolbar != null)
			{
				self.toolbar.resetMode(true);
			}
		};
		this.editor.graph.addListener(mxEvent.DOUBLE_CLICK, this.resetHandler);
		this.editor.addListener(mxEvent.ESCAPE, this.resetHandler);
	}
};
mxDefaultToolbar.prototype.addItem = function(title, icon, action, pressed)
{
	var self = this;
	var clickHandler = function()
	{
		self.editor.execute(action);
	};
	return this.toolbar.addItem(title, icon, clickHandler, pressed);
};
mxDefaultToolbar.prototype.addSeparator = function(icon)
{
	icon = icon || mxClient.imageBasePath + 'separator.gif';
	this.toolbar.addSeparator(icon);
};
mxDefaultToolbar.prototype.addCombo = function()
{
	return this.toolbar.addCombo();
};
mxDefaultToolbar.prototype.addActionCombo = function(title)
{
	return this.toolbar.addActionCombo(title);
};
mxDefaultToolbar.prototype.addActionOption = function(combo, title, action)
{
	var self = this;
	var clickHandler = function()
	{
		self.editor.execute(action);
	};
	this.addOption(combo, title, clickHandler);
};
mxDefaultToolbar.prototype.addOption = function(combo, title, value)
{
	return this.toolbar.addOption(combo, title, value);
};
mxDefaultToolbar.prototype.addMode = function(title, icon, mode, pressed, funct)
{
	var self = this;
	var clickHandler = function()
	{
		self.editor.setMode(mode);

		if (funct != null)
		{
			funct(self.editor);
		}
	};
	return this.toolbar.addSwitchMode(title, icon, clickHandler, pressed);
};
mxDefaultToolbar.prototype.addPrototype = function(title, icon, ptype, pressed, insert)
{
	var img = null;

	if (ptype == null)
	{
		img = this.toolbar.addMode(title, icon, null, pressed);
	}
	else
	{

		var factory = function()
		{
			if (typeof (ptype) == 'function')
			{
				return ptype();
			}
			else
			{
				return ptype.clone();
			}
		};

		var self = this;
		var clickHandler = function(evt, cell)
		{
			if (typeof (insert) == 'function')
			{
				insert(self.editor, factory(), evt, cell);
			}
			else
			{
				self.drop(factory(), evt, cell);
			}
			self.toolbar.resetMode();
			mxEvent.consume(evt);
		};
		img = this.toolbar.addMode(title, icon, clickHandler, pressed);

		var dropHandler = function(graph, evt, cell)
		{
			clickHandler(evt, cell);
		};
		this.installDropHandler(img, dropHandler);
	}
	return img;
};
mxDefaultToolbar.prototype.drop = function(vertex, evt, target)
{
	var graph = this.editor.graph;
	var model = graph.getModel();

	if (target == null || model.isEdge(target) || !this.connectOnDrop || !graph.isCellConnectable(target))
	{
		while (target != null && !graph.isValidDropTarget(target, [vertex], evt))
		{
			target = model.getParent(target);
		}
		this.insert(vertex, evt, target);
	}
	else
	{
		this.connect(vertex, evt, target);
	}
};
mxDefaultToolbar.prototype.insert = function(vertex, evt, target)
{
	var graph = this.editor.graph;

	if (graph.canImportCell(vertex))
	{
		var pt = mxUtils.convertPoint(graph.container, evt.clientX, evt.clientY);

		if (graph.isSplitEnabled() && graph.isSplitTarget(target, [vertex], evt))
		{
			return graph.splitEdge(target, [vertex], null, pt.x, pt.y);
		}
		else
		{
			return this.editor.addVertex(target, vertex, pt.x, pt.y);
		}
	}
	return null;
};
mxDefaultToolbar.prototype.connect = function(vertex, evt, source)
{
	var graph = this.editor.graph;
	var model = graph.getModel();

	if (source != null && graph.isCellConnectable(vertex) && graph.isEdgeValid(null, source, vertex))
	{
		var edge = null;
		model.beginUpdate();

		try
		{
			var geo = model.getGeometry(source);
			var g = model.getGeometry(vertex).clone();

			g.x = geo.x + (geo.width - g.width) / 2;
			g.y = geo.y + (geo.height - g.height) / 2;
			var step = this.spacing * graph.gridSize;
			var dist = model.getDirectedEdgeCount(source, true) * 20;

			if (this.editor.horizontalFlow)
			{
				g.x += (g.width + geo.width) / 2 + step + dist;
			}
			else
			{
				g.y += (g.height + geo.height) / 2 + step + dist;
			}
			vertex.setGeometry(g);

			var parent = model.getParent(source);
			graph.addCell(vertex, parent);
			graph.constrainChild(vertex);

			edge = this.editor.createEdge(source, vertex);

			if (model.getGeometry(edge) == null)
			{
				var edgeGeometry = new mxGeometry();
				edgeGeometry.relative = true;
				model.setGeometry(edge, edgeGeometry);
			}
			graph.addEdge(edge, parent, source, vertex);
		}
		finally
		{
			model.endUpdate();
		}
		graph.setSelectionCells(
		[
			vertex,
			edge
		]);

		graph.scrollCellToVisible(vertex);
	}
};
mxDefaultToolbar.prototype.installDropHandler = function(img, dropHandler)
{
	var sprite = document.createElement('img');
	sprite.setAttribute('src', img.getAttribute('src'));
	var self = this;
	var loader = function(evt)
	{
		sprite.style.width = (2 * img.offsetWidth) + 'px';
		sprite.style.height = (2 * img.offsetHeight) + 'px';
		mxUtils.makeDraggable(img, self.editor.graph, dropHandler, sprite);
		mxEvent.removeListener(sprite, 'load', loader);
	};

	if (mxClient.IS_IE)
	{
		loader();
	}
	else
	{
		mxEvent.addListener(sprite, 'load', loader);
	}
};
mxDefaultToolbar.prototype.destroy = function()
{
	if (this.resetHandler != null)
	{
		this.editor.graph.removeListener('dblclick', this.resetHandler);
		this.editor.removeListener('escape', this.resetHandler);
		this.resetHandler = null;
	}

	if (this.toolbar != null)
	{
		this.toolbar.destroy();
		this.toolbar = null;
	}
};